#include "basic_typedef.h"

/* 
	All types are defined in basic_typedef
*/


int main(int argc, char* argv[])
{
	/*
		1. Point
		http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Kernel_23_ref/Class_Point_2.html
	*/
	// Create Point with the coordinates (0.5, 0.6)
	Point p(0.5, 0.6);

	// Now, extract the x and y values into two variables of type Number_type
	Number_type x = p.x();
	Number_type y = p.y();
	// Use the function CGAL::to_double (or .to_double function of Number_type)
	// to extract the double values of the x and y and print them
	double x_double = x.to_double();
	double y_double = y.to_double();
	/*
	2. Polygon 
	http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Polygon_ref/Class_Polygon_2.html
	*/

	// Create a polygon from the points (-0.4,0.3) (-1 , 1) (0, 2) (0.7, 0.1)
	Point points[] = { Point(-0.4, 0.3), Point(-1, 1), Point(0, 2), Point(0.7, 0.1)};

	Polygon po(points, points+4);

	// Find the bounding box of the polygon (this is the smallest rectangle [parallel to axes]
	// that contains the polygon) by traversing the points of the polygon using vertices_begin,vertices_end iterators
	// Use procedures that are provided by Number_type (do not work with the double values)

	Number_type max_up;
	Number_type max_down;
	Number_type max_left;
	Number_type max_right;

	for ( Polygon::Vertex_iterator v = po.vertices_begin(); v != po.vertices_end(); ++v)
	{
		std::cout << v->x() << "," << v->y() << std::endl;

		if (v->y() > max_up)
		{
			max_up = v->y();
		}

		if (v->y() < max_down)
		{
			max_down = v->y();
		}

		if (v->x() < max_left)
		{
			max_left = v->x();
		}

		if (v->x() > max_right)
		{
			max_right = v->x();
		}
	}

	Point right_up(max_right,max_up);
	Point right_down(max_right,max_down);
	Point left_down(max_left,max_down);
	Point left_up(max_left,max_up);


	std::cout << "right_up - " << right_up.x() << "," << right_up.y() << std::endl;

	std::cout << "right_down - " << right_down.x() << "," << right_down.y() << std::endl;

	std::cout << "left_down - " << left_down.x() << "," << left_down.y() << std::endl;

	std::cout << "left_up - " << left_up.x() << "," << left_up.y() << std::endl;

	/* 
		3. Polygon_set 
		http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Boolean_set_operations_2_ref/Class_General_polygon_set_2.html
	*/
	
	// Create an empty polygon set (Polygon_set)
	Polygon_set UNION;

	// Create the polygons A,B,C,D = {(1,1), (2,2), (3,1)}, {(-1,1), (-2,2), (-3,1)},{(-2,1.5), (2,1.5), (2,0), (-2,0)}, 
	//								{(-1,-1),(-2,-2),(2,-2),(1,-1)}
	// and another polygon E = {(-1.8, 4) (1.8,4) (1.8, -4) (-1.8, -4)} 
	// (add the points to the polygons according to the order in which they are written)
	Point points_A[] = { Point(1, 1), Point(2, 2), Point(3, 1)};
	Point points_B[] = { Point(-1, 1), Point(-2, 2), Point(-3, 1)};
	Point points_C[] = { Point(-2, 1.5), Point(2, 1.5), Point(2, 0), Point(-2, 0)};
	Point points_D[] = { Point(-1, -1), Point(-2, -2), Point(2, -2), Point(1, -1)};
	Point points_E[] = { Point(-1.8, 4), Point(1.8, 4), Point(1.8, -4), Point(-1.8, -4)};

	Polygon polygon_A(points_A, points_A + 3);
	Polygon polygon_B(points_B, points_B + 3);
	Polygon polygon_C(points_C, points_C + 4);
	Polygon polygon_D(points_D, points_D + 4);
	Polygon polygon_E(points_E, points_E + 4);


	// In order to use the polygon_set the polygons must be counter-clockwise oriented
	// For every polygon A,B,C,D,E check that it is counter-clockwise using the "is_counterclockwise_oriented()" predicate
	// In case it's not, use "reverse_orientation()"
	if (!polygon_A.is_counterclockwise_oriented())
	{
		polygon_A.reverse_orientation();
	}

	if (!polygon_B.is_counterclockwise_oriented())
	{
		polygon_B.reverse_orientation();
	}

	if (!polygon_C.is_counterclockwise_oriented())
	{
		polygon_C.reverse_orientation();
	}

	if (!polygon_D.is_counterclockwise_oriented())
	{
		polygon_D.reverse_orientation();
	}

	if (!polygon_E.is_counterclockwise_oriented())
	{
		polygon_E.reverse_orientation();
	}


	// Insert A,B,C,D into a polygon set called UNION using the "join" operation ("insert" can be used only if the polygons are disjoint)
	// Find the intersection of E with UNION set using the "intersection" operation of the polygon_set
	// Denote this intersection by INTER
	UNION.join(polygon_A);
	UNION.join(polygon_B);
	UNION.join(polygon_C);
	UNION.join(polygon_D);

	Polygon_set INTER = UNION;
	INTER.intersection(polygon_E);
	// Determine whether each of the points (1.5,2.6),(1.9,0.09) is located inside UNION (using oriented_side)
	if (UNION.oriented_side(Point(1.5,2.6)))
	{
		std::cout << "The point (1.5,2.6) is located inside UNION";
	}

	if (UNION.oriented_side(Point(1.9,0.09)))
	{
		std::cout << "The point (1.9,0.09) is located inside UNION";
	}

	/*
		4. Polygon_with_holes
		http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Boolean_set_operations_2_ref/Concept_GeneralPolygonWithHoles_2.html#Cross_link_anchor_1005
	*/

	// The result INTER is represented as a collection of Polygon_with_holes
	// Extract all the Polygon_with_holes that define INTER and for each polygon 
	// Print the vertices that define its boundary
	
	/*	
		hint:
		Polygon_set ps; 
		Polygon_with_holes_container container; 
		ps.polygons_with_holes (std::back_inserter (container));
	*/ 

	std::list<Polygon_with_holes> res;
	std::list<Polygon_with_holes>::const_iterator it;

	INTER.polygons_with_holes (std::back_inserter (res));
	for (it = res.begin(); it != res.end(); ++it)
	{
		Polygon::Vertex_const_iterator vertices_it;

		Polygon current_polygon = it->outer_boundary();

		std::cout << "[ " << current_polygon.size() << " vertices:";
		for (vertices_it = current_polygon.vertices_begin(); vertices_it != current_polygon.vertices_end(); ++vertices_it)
		{
			std::cout << " (" << *vertices_it << ')';
		}
		std::cout << " ]" << std::endl;
	}

	/*
		5. Arrangement
		http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Arrangement_on_surface_2_ref/Class_Arrangement_2.html#Cross_link_anchor_1186
	*/
	//	Extract the underlying arrangement of the INTER polygon set
	//	Traverse the faces of the arrangement and print the vertices of each face

	Arrangement underlying_arrangement = INTER.arrangement();

	std::cout << "Number of faces: " << underlying_arrangement.number_of_faces() << "\n";

	for(Arrangement::Face_iterator face_iterator = underlying_arrangement.faces_begin();
		face_iterator != underlying_arrangement.faces_end();
		++face_iterator)
	{
		std::cout << "Number of isolated vertices: " << face_iterator->number_of_isolated_vertices() << "\n";
		
		for(Arrangement::Face::Isolated_vertex_iterator vertices_iterator = face_iterator->isolated_vertices_begin();
			vertices_iterator != face_iterator->isolated_vertices_end();
			vertices_iterator++)
		{
			std::cout << "Vertex - " << vertices_iterator->point() << "\n";
		}
	}  

	/*
		6. Minkowski Sums and Arrangements 
		http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Minkowski_sum_2/Chapter_main.html
	*/
	//	Generate the polygons	P = {(0,0)(-2,0)(-3,-1)(-3,-4),(-2,-5)(3,-5)(4,-4)(4,-1)(3,0)(1,0)(1,-1),
	//								(3,-1)(3,-4)(-2,-4)(-2,-1)(0,-1)}
	//							Q = {(1,1)(1,0)(0,0)(0,1)}

	Point points_P[] = {
		Point(0,0),
		Point(-2,0), 
		Point(-3,-1), 
		Point(-3,-4),
		Point(-2,-5), 
		Point(3,-5),
		Point(4,-4), 
		Point(4,-1), 
		Point(3,0), 
		Point(1,0), 
		Point(1,-1),
		Point(3,-1), 
		Point(3,-4), 
		Point(-2,-4), 
		Point(-2,-1), 
		Point(0,-1)};

		Point points_Q[] = {
			Point(1,1),
			Point(1,0), 
			Point(0,0), 
			Point(0,1)};

	//	Find the Minkowski sums of P and Q
	Polygon polygon_P(points_P, points_P + 16);
	Polygon polygon_Q(points_Q, points_Q + 4);

	//	Denote the result by MINK
	Polygon_with_holes MINK = minkowski_sum_2(polygon_P, polygon_Q);

	//	Print the faces of MINK and its complement

	std::cout << "Number of holes: " << MINK.number_of_holes() << "\n";

	for(Polygon_with_holes::Hole_iterator holes_iterator = MINK.holes_begin();
		holes_iterator != MINK.holes_end();
		++holes_iterator)
	{
		std::cout << "Current hole vertices:\n";
		for(Polygon::Vertex_const_iterator vertices_iterator = holes_iterator->vertices_begin();
			vertices_iterator != holes_iterator->vertices_end();
			++vertices_iterator)
		{
			std::cout << "Vertex - " << (*vertices_iterator) << "\n";
		}
	}

	std::cout<<"Vertices of the minkovski sum outter bounds:\n";
	for(Polygon::Vertex_const_iterator vertices_iterator = MINK.outer_boundary().vertices_begin();
		vertices_iterator != MINK.outer_boundary().vertices_end();
		++vertices_iterator)
	{
		std::cout << (*vertices_iterator) << "\n";
	}
	return 0;
}

 